package web

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"strconv"
)

type Context struct {
	Req  *http.Request
	Resp http.ResponseWriter

	PathParams  map[string]string
	MathedRoute string

	RespStatusCode int
	RespData       []byte

	cacheQueryValues url.Values

	tplEngine TemplateEngine

	// 缓存 session
	UserValues map[string]any
}

// 代理 tplEngine 的 Render 方法，可以在这里做一些其它操作
// 也可以不要这个代理
func (c *Context) Render(tplName string, data any) error {
	var err error
	c.RespData, err = c.tplEngine.Render(c.Req.Context(), tplName, data)
	if err != nil {
		c.RespStatusCode = http.StatusInternalServerError
		return err
	}

	c.RespStatusCode = http.StatusOK
	return nil
}

func (c *Context) BindJSON(val interface{}) error {
	if c.Req.Body == nil {
		return errors.New("web: body 为空")
	}

	// 这样处理不好
	// body, _ := io.ReadAll(c.Req.Body)
	// json.Unmarshal(body, val)

	dec := json.NewDecoder(c.Req.Body)
	return dec.Decode(val)
}

// 获取表单参数
func (c *Context) FormValue(key string) StringValue {
	if err := c.Req.ParseForm(); err != nil {
		return StringValue{err: err}
	}

	return StringValue{val: c.Req.FormValue(key)}
}

// 获取查询参数
func (c *Context) QueryValue(key string) StringValue {
	if c.cacheQueryValues == nil {
		c.cacheQueryValues = c.Req.URL.Query()
	}
	vals, ok := c.cacheQueryValues[key]
	if !ok {
		return StringValue{err: errors.New("web: 找不到这个 key")}
	}

	return StringValue{val: vals[0]}
}

// 获取路径参数
func (c *Context) PathValue(key string) StringValue {
	val, ok := c.PathParams[key]
	if !ok {
		return StringValue{err: errors.New("web: 找不到这个 key")}
	}

	return StringValue{val: val}
}

func (c *Context) SetCookie(cookie *http.Cookie) {
	http.SetCookie(c.Resp, cookie)
}

func (c *Context) RespJSON(code int, val any) error {
	b, err := json.Marshal(val)
	if err != nil {
		return err
	}

	c.RespStatusCode = code
	c.RespData = b
	return err
}

func (c *Context) RespJSONOK(val any) error {
	return c.RespJSON(http.StatusOK, val)
}

type StringValue struct {
	val string
	err error
}

func (s *StringValue) String() (string, error) {
	return s.val, s.err
}

func (s *StringValue) ToInt64() (int64, error) {
	if s.err != nil {
		return 0, s.err
	}

	return strconv.ParseInt(s.val, 10, 64)
}
